// Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.

// 伸缩组辅助数据表定义
package db

import (
	"context"
	"github.com/beego/beego/v2/client/orm"
	"github.com/pkg/errors"

	"scase.io/application-auto-scaling-service/pkg/common"
	"scase.io/application-auto-scaling-service/pkg/utils/logger"
)

// TxRecordGroupScaleOutStart 记录伸缩组扩容开始（事务）
func TxRecordGroupScaleOutStart(groupId string, targetNum int32) error {
	return ormer.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
		// 1. 更新伸缩组状态 stable -> scaling
		num, err := changeScalingGroupState(txOrm, groupId,
			ScalingGroupStateStable, ScalingGroupStateScaling)
		if err != nil {
			return err
		}
		// 伸缩组非稳定状态（包含伸缩组不存在的情况）
		if num != 1 {
			return errors.Wrapf(common.ErrScalingGroupNotStable,
				"scaling group[%s] is unstable or do not exist", groupId)
		}

		// 2. 记录扩容任务
		group := &ScalingGroup{Id: groupId}
		err = txOrm.QueryTable(tableNameScalingGroup).
			Filter(fieldNameId, groupId).
			One(group, fieldNameProjectId)
		if err != nil {
			return errors.Wrapf(err, "get project id for group[%s] err", groupId)
		}
		return txInsertAsyncTask(txOrm, newAsyncTaskForScaleOut(groupId, group.ProjectId, targetNum))
	})
}

// TxRecordGroupScaleOutComplete 记录伸缩组扩容完成（事务）
func TxRecordGroupScaleOutComplete(groupId string) error {
	return ormer.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
		// 1. 更新伸缩组状态 scaling -> stable
		_, err := changeScalingGroupState(txOrm, groupId,
			ScalingGroupStateScaling, ScalingGroupStateStable)
		if err != nil {
			return err
		}

		// 2. 删除扩容任务
		return txDeleteAsyncTask(txOrm, TaskTypeScaleOutScalingGroup, groupId)
	})
}

// TxRecordGroupScaleInStart 记录伸缩组缩容开始（事务）
func TxRecordGroupScaleInStart(groupId string, scaleInInstanceIds []string) error {
	return ormer.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
		// 1. 更新伸缩组状态 stable -> scaling
		num, err := changeScalingGroupState(txOrm, groupId,
			ScalingGroupStateStable, ScalingGroupStateScaling)
		if err != nil {
			return err
		}
		// 伸缩组非稳定状态（包含伸缩组不存在的情况）
		if num != 1 {
			return errors.Wrapf(common.ErrScalingGroupNotStable,
				"scaling group[%s] is unstable or do not exist", groupId)
		}

		// 2. 记录扩容任务
		group := &ScalingGroup{Id: groupId}
		err = txOrm.QueryTable(tableNameScalingGroup).
			Filter(fieldNameId, groupId).
			One(group, fieldNameProjectId)
		if err != nil {
			return errors.Wrapf(err, "get project id for group[%s] err", groupId)
		}
		return txInsertAsyncTask(txOrm, newAsyncTaskForScaleIn(groupId, group.ProjectId, scaleInInstanceIds))
	})
}

// TxRecordGroupScaleInComplete 记录伸缩组缩容完成（事务）
func TxRecordGroupScaleInComplete(groupId string) error {
	return ormer.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
		// 1. 更新伸缩组状态 scaling -> stable
		_, err := changeScalingGroupState(txOrm, groupId,
			ScalingGroupStateScaling, ScalingGroupStateStable)
		if err != nil {
			return err
		}

		// 2. 删除扩容任务
		return txDeleteAsyncTask(txOrm, TaskTypeScaleInScalingGroup, groupId)
	})
}

// ChangeScalingGroupState2Deleting 将伸缩组状态更新为"deleting"，即伸缩组删除开始
// 若当前伸缩组不可删除，返回错误 ErrScalingGroupCannotBeDeleted
func ChangeScalingGroupState2Deleting(log *logger.FMLogger, groupId string) error {
	return ormer.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
		// 1. 校验当前group的状态
		group := &ScalingGroup{
			Id:        groupId,
			TimeModel: TimeModel{IsDeleted: notDeletedFlag},
		}
		err := txOrm.ReadForUpdate(group)
		if err != nil {
			return errors.Wrapf(err, "read group[%s] from db err", groupId)
		}
		// 若处于“deleting”，无需处理
		if group.State == ScalingGroupStateDeleting {
			log.Info("Group[%s] state is deleting, do nothing", groupId)
			return nil
		}
		// 目前仅有“stable”和“error”状态时，group才可被删除
		if group.State != ScalingGroupStateStable && group.State != ScalingGroupStateError {
			log.Info("Group[%s] cannot be deleted in state[%s]", groupId, group.State)
			return common.ErrScalingGroupCannotBeDeleted
		}

		// 2. 更新状态为“deleting”
		group.State = ScalingGroupStateDeleting
		_, err = txOrm.Update(group, "State")
		if err != nil {
			return errors.Wrapf(err, "update group[%s] state to deleting err", groupId)
		}
		return nil
	})
}

// TxRecordGroupDeletingComplete 软删除伸缩组相关信息，并记录伸缩组删除任务结束
func TxRecordGroupDeletingComplete(log *logger.FMLogger, groupId string) error {
	return ormer.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
		// 1. 软删除ScalingGroup及其附属对象
		err := DeleteScalingGroup(txOrm, groupId)
		if err != nil {
			return err
		}

		// 2. 删除异步任务
		return txDeleteAsyncTask(txOrm, TaskTypeDeleteScalingGroup, groupId)
	})
}

// changeScalingGroupState 将伸缩组状态由 oldState 改为 newState
// 若group不存在，不会返回error
func changeScalingGroupState(txOrm orm.TxOrmer, groupId, oldState, newState string) (int64, error) {
	num, err := txOrm.QueryTable(tableNameScalingGroup).
		Filter(fieldNameId, groupId).
		Filter(fieldNameIsDeleted, notDeletedFlag).
		Filter(fieldNameState, oldState).
		Update(orm.Params{
			fieldNameState: newState,
		})
	if err != nil {
		return 0, errors.Wrapf(err, "update scaling group[%s] state to [%s] err", groupId, newState)
	}
	return num, nil
}

// 数据库记录扩容任务开始
func TxRecordEcsGroupScalingOutStart(taskId string, groupId string, instanceNum int32) error {
	return ormer.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
		// 1. 更新伸缩组stable/scaling状态为 scaling，
		group := ScalingGroup{}
		if err := txOrm.QueryTable(tableNameScalingGroup).
			Filter(fieldNameId, groupId).One(&group); err != nil {
			return errors.Wrapf(common.ErrScalingGroupNotExist, "scaling group[%s] do not exist", groupId)
		}
		if group.State == ScalingGroupStateStable {
			num, err := txOrm.QueryTable(tableNameScalingGroup).
				Filter(fieldNameId, groupId).
				Filter(fieldNameIsDeleted, notDeletedFlag).
				Filter(fieldNameState, ScalingGroupStateStable).
				Update(orm.Params{
					fieldNameState: ScalingGroupStateScaling,
				})
			if err != nil {
				return errors.Wrapf(err, "update scaling group[%s] state to [%s] err", groupId, ScalingGroupStateScaling)
			}
			if num != 1 {
				return errors.Wrapf(common.ErrScalingGroupNotExist, "scaling group[%s] do not exist", groupId)
			}
		}

		// 2. 记录扩容任务
		return txInsertAsyncTask(txOrm, newAsyncTaskForEcsGroupScaleOut(taskId, groupId, instanceNum))
	})
}

// 数据库记录pod扩容任务开始
func TxRecordPodGroupScalingOutStart(taskId string, groupId string, instanceNum int32) error {
	return ormer.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
		// 1. 更新伸缩组stable/scaling状态为 scaling，
		group := ScalingGroup{}
		if err := txOrm.QueryTable(tableNameScalingGroup).
			Filter(fieldNameId, groupId).One(&group); err != nil {
			return errors.Wrapf(common.ErrScalingGroupNotExist, "scaling group[%s] do not exist", groupId)
		}
		if group.State == ScalingGroupStateStable {
			num, err := txOrm.QueryTable(tableNameScalingGroup).
				Filter(fieldNameId, groupId).
				Filter(fieldNameIsDeleted, notDeletedFlag).
				Filter(fieldNameState, ScalingGroupStateStable).
				Update(orm.Params{
					fieldNameState: ScalingGroupStateScaling,
				})
			if err != nil {
				return errors.Wrapf(err, "update scaling group[%s] state to [%s] err", groupId, ScalingGroupStateScaling)
			}
			if num != 1 {
				return errors.Wrapf(common.ErrScalingGroupNotExist, "scaling group[%s] do not exist", groupId)
			}
		}

		// 2. 记录扩容任务
		return txInsertAsyncTask(txOrm, newAsyncTaskForPodGroupScaleOut(taskId, groupId, instanceNum))
	})
}

// 数据库记录VM缩容任务开始
func TxRecordEcsGroupScalingInStart(taskId, groupId string, scaleInInstanceIds []string) error {
	return ormer.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
		// 1. 更新伸缩组状态 stable -> scaling
		num, err := txOrm.QueryTable(tableNameScalingGroup).
			Filter(fieldNameId, groupId).
			Filter(fieldNameIsDeleted, notDeletedFlag).
			Filter(fieldNameState, ScalingGroupStateStable).
			Update(orm.Params{
				fieldNameState: ScalingGroupStateScaling,
			})
		if err != nil {
			return errors.Wrapf(err, "update scaling group[%s] state to [%s] err", groupId, ScalingGroupStateScaling)
		}
		// 伸缩组非稳定状态（包含伸缩组不存在的情况）
		if num != 1 {
			return errors.Wrapf(common.ErrScalingGroupNotStable,
				"scaling group[%s] is unstable or do not exist", groupId)
		}

		// 2. 记录扩容任务

		return txInsertAsyncTask(txOrm, newAsyncTaskForEcsGroupScaleIn(taskId, groupId, scaleInInstanceIds))
	})
}

// 数据库记录扩容/缩容任务结束
func TxRecordEcsGroupScalingTaskComplete(groupId, taskId string) error {
	return ormer.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
		// 1. 通过taskId删除任务
		err := txDeleteAsyncTaskByTaskId(txOrm, taskId)
		if err != nil {
			return err
		}

		// 查一下该group是否还有在进行的任务，如果有则不置为stable，如果无则置stable
		if checkExistTaskKey(txOrm, groupId) {
			logger.R.Info("group [%s] has other task, doing nothing")
			return nil
		}
		// 2. 更新伸缩组状态 scaling -> stable
		_, err = txOrm.QueryTable(tableNameScalingGroup).
			Filter(fieldNameId, groupId).
			Filter(fieldNameIsDeleted, notDeletedFlag).
			Filter(fieldNameState, ScalingGroupStateScaling).
			Update(orm.Params{
				fieldNameState: ScalingGroupStateStable,
			})
		if err != nil {
			return errors.Wrapf(err, "update scaling group[%s] state to [%s] err", groupId, ScalingGroupStateStable)
		}
		return nil
	})
}

// 数据库记录pod缩容任务开始
func TxRecordPodGroupScalingInStart(taskId, groupId string, scaleInPods []string) error {
	return ormer.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
		// 1. 更新伸缩组状态 stable -> scaling
		num, err := txOrm.QueryTable(tableNameScalingGroup).
			Filter(fieldNameId, groupId).
			Filter(fieldNameIsDeleted, notDeletedFlag).
			Filter(fieldNameState, ScalingGroupStateStable).
			Update(orm.Params{
				fieldNameState: ScalingGroupStateScaling,
			})
		if err != nil {
			return errors.Wrapf(err, "update scaling group[%s] state to [%s] err", groupId, ScalingGroupStateScaling)
		}
		// 伸缩组非稳定状态（包含伸缩组不存在的情况）
		if num != 1 {
			return errors.Wrapf(common.ErrScalingGroupNotStable,
				"scaling group[%s] is unstable or do not exist", groupId)
		}

		// 2. 记录扩容任务

		return txInsertAsyncTask(txOrm, newAsyncTaskForPodGroupScaleIn(taskId, groupId, scaleInPods))
	})
}

// 数据库记录pod扩容/缩容任务结束
func TxRecordPodGroupScalingTaskComplete(groupId, taskId string) error {
	return ormer.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
		// 1. 通过taskId删除任务
		err := txDeleteAsyncTaskByTaskId(txOrm, taskId)
		if err != nil {
			return err
		}

		// 查一下该group是否还有在进行的任务，如果有则不置为stable，如果无则置stable
		if checkExistTaskKey(txOrm, groupId) {
			logger.R.Info("group [%s] has other task, doing nothing")
			return nil
		}
		// 2. 更新伸缩组状态 scaling -> stable
		_, err = txOrm.QueryTable(tableNameScalingGroup).
			Filter(fieldNameId, groupId).
			Filter(fieldNameIsDeleted, notDeletedFlag).
			Filter(fieldNameState, ScalingGroupStateScaling).
			Update(orm.Params{
				fieldNameState: ScalingGroupStateStable,
			})
		if err != nil {
			return errors.Wrapf(err, "update scaling group[%s] state to [%s] err", groupId, ScalingGroupStateStable)
		}
		return nil
	})
}

// 数据库记录删除伸缩组开始
// 若该任务已存在（删除伸缩组api的可重入，会导致重复插入的情况），返回错误 ErrDeleteTaskAlreadyExists
func TxInsertDeleteECSScalingGroupTask(taskId, groupId string) error {
	return ormer.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
		// 1. 判断若task已存在，无需insert，返回错误 ErrAsyncTaskAlreadyExists
		exist := txOrm.QueryTable(tableNameAsyncTask).
			Filter(fieldNameTaskKey, groupId).
			Filter(fieldNameTaskType, TaskTypeDeleteEcsScalingGroup).
			Filter(fieldNameIsDeleted, notDeletedFlag).
			Exist()
		if exist {
			return errors.Wrapf(common.ErrDeleteTaskAlreadyExists,
				"delete group task for group[%s] already exists", groupId)
		}

		// 2. 执行task插入操作
		task := newAsyncTaskForDeleteEcsScalingGroup(taskId, groupId)
		task.IsDeleted = notDeletedFlag
		_, err := txOrm.Insert(task)
		if err != nil {
			return errors.Wrapf(err, "db add async task[%s:%s] err", task.TaskType, task.TaskKey)
		}
		return nil
	})
}

// 数据库记录删除伸缩组任务结束
func TxRecordDeleteEcsGroupTaskComplete(groupId string) error {
	return ormer.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
		// 1. 软删除ScalingGroup及其附属对象
		err := ScalingGroupTable().TxDeleteScalingGroup(txOrm, groupId)
		if err != nil {
			return err
		}
		// 2. 删除异步任务
		return txDeleteAsyncTask(txOrm, TaskTypeDeleteEcsScalingGroup, groupId)
	})
}

// 数据库记录删除命名空间任务结束
func TxRecordDeletePodNamespaceTaskComplete(groupId string) error {
	return ormer.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
		// 1. 软删除ScalingGroup及其附属对象
		err := ScalingGroupTable().TxDeleteScalingGroup(txOrm, groupId)
		if err != nil {
			return err
		}
		// 2. 删除异步任务
		return txDeleteAsyncTask(txOrm, TaskTypeDeleteEcsScalingGroup, groupId)
	})
}

// 数据库记录删除伸缩组开始
// 若该任务已存在（删除伸缩组api的可重入，会导致重复插入的情况），返回错误 ErrDeleteTaskAlreadyExists
func TxInsertDeletePodNamespaceTask(taskId, groupId string) error {
	return ormer.DoTx(func(ctx context.Context, txOrm orm.TxOrmer) error {
		// 1. 判断若task已存在，无需insert，返回错误 ErrAsyncTaskAlreadyExists
		exist := txOrm.QueryTable(tableNameAsyncTask).
			Filter(fieldNameTaskKey, groupId).
			Filter(fieldNameTaskType, TaskTypeDeleteNamespace).
			Filter(fieldNameIsDeleted, notDeletedFlag).
			Exist()
		if exist {
			return errors.Wrapf(common.ErrDeleteTaskAlreadyExists,
				"delete namespace task for group[%s] already exists", groupId)
		}

		// 2. 执行task插入操作
		task := newAsyncTaskForDeletePodNamespace(taskId, groupId)
		task.IsDeleted = notDeletedFlag
		_, err := txOrm.Insert(task)
		if err != nil {
			return errors.Wrapf(err, "db add async task[%s:%s] err", task.TaskType, task.TaskKey)
		}
		return nil
	})
}
